﻿using System;
using System.Threading;
using System.Threading.Tasks;
using CCWin;
using log4net;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
using System.Diagnostics;
using System.Text;
using Stepon.FaceRecognization.Recognization;
using AForge.Video.DirectShow;
using System.Drawing.Imaging;

namespace IdCardFaceIdentifier
{

    public partial class Form1 : Skin_Mac
    {
        public class MessageState
        {

            public MessageState(Bitmap bitmap, string message, string time, Bitmap snapshot)
            {
                this.bitmap = bitmap;
                this.message = message;
                this.time = time;
                this.snapshot = snapshot;
            }

            public Bitmap bitmap { get; set; }
            public string message { get; set; }
            public string time { get; set; }
            public Bitmap snapshot { get; set; }

        }

        private static readonly ILog log = LogManager.GetLogger(typeof(Form1));
        private static object synCurrentMat = new object();

        private CancellationTokenSource captureToken = new CancellationTokenSource();

        private ArcFaceApi arcFaceApi;
        private Bitmap currentFrame;
        private static object sync = new object();

        private VideoCaptureDevice videoCaptureDevice;
        private bool isreadyCompare = false;
        private SynchronizationContext m_SyncContext;

        public Form1()
        {
            InitializeComponent();
            m_SyncContext = SynchronizationContext.Current;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            FilterInfoCollection filters = new FilterInfoCollection(FilterCategory.VideoInputDevice);
            videoCaptureDevice = new VideoCaptureDevice(filters[0].MonikerString);
            videoCaptureDevice.NewFrame += VideoCaptureDevice_NewFrame;
            videoCaptureDevice.Start();
            log.Info("usb相机采集开始");

            arcFaceApi = new ArcFaceApi();

            new Task(ValidFace, captureToken.Token).Start();
            new Task(ReadCard, captureToken.Token).Start();

            try
            {
                serialPort1.Open();
            }
            catch (Exception ex)
            {
                MessageBox.Show("打开COM1失败");
            }
        }

        private void VideoCaptureDevice_NewFrame(object sender, AForge.Video.NewFrameEventArgs eventArgs)
        {
            Bitmap newFrame = (Bitmap)eventArgs.Frame.Clone();
            //如果使用视频文件请注释下面的这行代码，表示不对图像进行水平翻转
            newFrame.RotateFlip(RotateFlipType.Rotate180FlipY);
            lock (sync)
            {
                if (currentFrame != null)
                {
                    currentFrame.Dispose();
                    currentFrame = null;
                }
                currentFrame = newFrame;
                skinPictureBox2.Invalidate();
            }
        }

        /**
         * 每间隔1s连续读证，读到证件后待人脸认证完后再重新读证
         */
        private void ReadCard()
        {
            Task.Delay(1000);
            int CVR_InitComm = -1;
            while (!captureToken.IsCancellationRequested)
            {

                try
                {
                    if(isreadyCompare)
                    {
                        continue;
                    }
                    m_SyncContext.Post(CleanCardInfo, null);
                    log.Debug("开始读证");
                    if (CVR_InitComm != 0)
                    {
                        CVR_InitComm = CVRSDK.CVR_InitComm(1001);
                        log.DebugFormat("初始化USB CVR_InitComm({0}) 结果 {1}", 1001, CVR_InitComm);
                    }
                    //打开串口失败，一般硬件故障
                    if (CVR_InitComm != 1)
                    {
                        m_SyncContext.Post(UpdateMessageState, new MessageState(Properties.Resources.Red_Firewire_256,"阅读器故障","",null));
                        continue;
                    }

                    //身份证认证失败、一般为未读到卡片
                    int CVR_Authenticate = CVRSDK.CVR_Authenticate();
                    log.DebugFormat("身份证阅读器认证 CVR_Authenticate 结果 {0}", CVR_Authenticate);
                    if (CVR_Authenticate != 1)
                    {
                        m_SyncContext.Post(UpdateMessageState, new MessageState(Properties.Resources.Blue_Firewire_256, "请放身份证", "", null));
                        continue;
                    }

                    //身份证认证失败、一般为未读到卡片、或需要重新放卡
                    int CVR_Read_Content = CVRSDK.CVR_Read_Content(0);
                    log.DebugFormat("身份证信息读取 CVR_Read_Content 结果 {0}", CVR_Read_Content);
                    if (CVR_Read_Content != 1)
                    {
                        m_SyncContext.Post(UpdateMessageState, new MessageState(Properties.Resources.Blue_Firewire_256, "请放身份证", "", null));
                        continue;
                    }

                    m_SyncContext.Post(ReadCardInfo, null);
                    m_SyncContext.Post(UpdateMessageState, new MessageState(Properties.Resources.Blue_Firewire_256, "读证成功", "", null));
                    //延时一秒，让访客看清当前步骤
                    Task.Delay(1000);
                    isreadyCompare = true;
                }
                catch (Exception ex)
                {
                    log.Debug("读证异常", ex);
                }
                finally
                {
                    Thread.Sleep(1000);
                }
            }
        }

        /**
         * 当证件信息被读到时，实时认证人脸
         */
        private void ValidFace()
        {
            Task.Delay(1000);
            Stopwatch stopwatch = Stopwatch.StartNew();
            Bitmap snapshotImage = null;
            while (!captureToken.IsCancellationRequested)
            {
                try
                {
                    if (!isreadyCompare)
                    {
                        stopwatch.Restart();
                        continue;
                    }

                    if (stopwatch.Elapsed.Seconds > 10)
                    {
                        m_SyncContext.Post(UpdateMessageState, new MessageState(Properties.Resources.user_delete_256, "比对失败", "", null));
                        stopwatch.Restart();
                        isreadyCompare = false;
                        continue;
                    }

                    if (snapshotImage != null)
                    {
                        snapshotImage.Dispose();
                        snapshotImage = null;
                    }

                    log.Debug("比对身份证人脸");
                    snapshotImage = GetCurrentFrame();

                    m_SyncContext.Post(UpdateMessageState, new MessageState(Properties.Resources.user_search_256, "正在比对", "", null));

                    bool result = arcFaceApi.MatchOneToOne(snapshotImage);
                    log.DebugFormat("人脸比对结果 {0}", result);
                    if (result)
                    {
                        m_SyncContext.Post(UpdateMessageState, new MessageState(Properties.Resources.user_accept_256, "比对成功", "比对时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), AForge.Imaging.Image.Clone(snapshotImage)));
                        OpenDoor();
                        //验证成功后停留显示结果3秒
                        Thread.Sleep(3000);
                        stopwatch.Restart();
                        isreadyCompare = false;
                        continue;
                    }
                }
                catch (Exception ex)
                {
                    log.Warn("人脸比对异常", ex);
                }
                finally
                {
                    Thread.Sleep(500);
                }
            }
        }

        private void OpenDoor()
        {
            if (!serialPort1.IsOpen)
            {
                return;
            }
            try
            {
                var hex = "01 57 00 01 00 01 02 01 03 A6";
                string[] splits = hex.Split(' ');

                if (splits.Length == 0)
                {
                    return;
                }

                var inputByteArray = new byte[splits.Length];
                for (var x = 0; x < inputByteArray.Length; x++)
                {
                    var i = Convert.ToInt32(splits[x], 16);
                    inputByteArray[x] = (byte)i;
                }
                serialPort1.Write(inputByteArray, 0, inputByteArray.Length);
            }
            catch (Exception e)
            {
                log.Error("开闸失败", e);
            }
        }

        private Bitmap GetIDCardBitmap()
        {
            string path = Application.StartupPath + "\\CVRSDK\\zp.jpg";
            Bitmap bitmap = new Bitmap(path);
            Bitmap result = AForge.Imaging.Image.Clone(bitmap);
            bitmap.Dispose();
            return result;
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            captureToken.Cancel();
            videoCaptureDevice.Stop();
        }

        public void CleanCardInfo(object obj)
        {
            PeopleName.Text = "";
            PeopleSex.Text = "";
            PeopleNation.Text = "";
            PeopleBirthday.Text = "";
            PeopleIDCode.Text = "";
            Department.Text = "";
            ValidDate.Text = "";
            PeopleAddress.Text = "";
            skinPictureBox1.Image = null;
        }

        /**
         * 读取本地生成的二代证信息，读完后立即删除原文件
         */
        public void ReadCardInfo(object obj)
        {
            try
            {
                log.Debug("开始读本地证件资料");
                string path = Application.StartupPath + "\\CVRSDK\\zp.bmp";
                string path2 = Application.StartupPath + "\\CVRSDK\\wz.txt";
                string path3 = Application.StartupPath + "\\CVRSDK\\zp.jpg";

                if (!File.Exists(path2) || !File.Exists(path2))
                {
                    return;
                }

                string[] lines = File.ReadAllLines(path2, Encoding.GetEncoding("gb2312"));
                if (lines.Length != 8)
                {
                    CleanCardInfo(null);
                    return;
                }

                PeopleName.Text = lines[0].Trim();
                PeopleSex.Text = lines[1].Trim();
                PeopleNation.Text = lines[2].Trim();
                PeopleBirthday.Text = lines[3].Trim();
                PeopleAddress.Text = lines[4].Trim();
                PeopleIDCode.Text = lines[5].Trim();
                Department.Text = lines[6].Trim();
                ValidDate.Text = lines[7].Trim();

                log.InfoFormat("读二代证成功 {0}", lines);

                Bitmap bitmap = new Bitmap(path);
                bitmap.Save(path3, ImageFormat.Jpeg);
                skinPictureBox1.Image = bitmap;

                arcFaceApi.AddToCompare(GetIDCardBitmap());
            }
            catch (Exception ex)
            {
                log.Debug("读二代证失败", ex);
                return;
            }
        }

        /**
         * 绘制当前帧到控制，必须与获取当前帧互斥
         */
        private void skinPictureBox2_Paint(object sender, PaintEventArgs e)
        {
            lock (synCurrentMat)
            {
                Bitmap bitmap = GetCurrentFrame();
                if (bitmap != null)
                {
                    e.Graphics.DrawImage(bitmap, new Rectangle(0, 0, skinPictureBox2.Width, skinPictureBox2.Height), new Rectangle(0, 0, bitmap.Width, bitmap.Height), GraphicsUnit.Pixel);
                }
            }
        }

        public Bitmap GetCurrentFrame()
        {
            if (captureToken.Token.IsCancellationRequested) return null;
            lock (sync)
            {
                return (currentFrame == null || currentFrame.Width == 0 || currentFrame.Height == 0) ? null : AForge.Imaging.Image.Clone(currentFrame);
            }
        }

        public void UpdateMessageState(object obj)
        {
            try
            {
                MessageState messageState = (MessageState)obj;
                if (messageState.bitmap == null)
                {
                    return;
                }

                skinPictureBox3.Image = messageState.bitmap;
                skinLabel11.Text = messageState.message;
                skinLabel12.Text = messageState.time;
                skinPictureBox4.Image = messageState.snapshot;
                log.DebugFormat("更新状态成功 {0}", messageState.message);
            }
            catch (Exception ex)
            {
                log.Error("更新状态异常", ex);
            }
        }
    }
}
